﻿using CMS6Tools.Common.Email;
using CMS6Tools.Common.TFS;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace CMS6Tools.TFSTasks
{
    public abstract class TfsTask : ITask
    {
        public TfsTask()
            :this(null)
        {
        }

        public TfsTask(TfsTeamProjectCollection collection)
        {
            tfsTeamProjectCollection = collection;
            Authenticate();
        }

        static readonly string TfsCollectionURL = ConfigurationManager.AppSettings["tfsCollectionURL"];
        static readonly string UserNameWithDomain = ConfigurationManager.AppSettings["userNameWithDomain"];
        static readonly string Password = ConfigurationManager.AppSettings["password"];
        static readonly string ADLdapPath = ConfigurationManager.AppSettings["ADLdapPath"];
        static readonly string ADUserName = ConfigurationManager.AppSettings["ADUserName"];
        static readonly string ADPassword = ConfigurationManager.AppSettings["ADPassword"];
        const string TBDIterationName = "TBD"; 
        protected static readonly string[] AdministratorEmails = new[] { "yding@visioninternet.com" };
        
        protected TfsTeamProjectCollection tfsTeamProjectCollection = null;
        protected string SelectedProjectName { get { return "CMS 6"; } }

        public virtual void Execute(params string[] args)
        {
            ExecuteInternal(args);
        }

        public List<DEVIndividualTrackMatrix> RunDailyTrackForDev(Node iterationNode)
        {
            var workItemStore = tfsTeamProjectCollection.GetService<WorkItemStore>();

            var proxy = WebAccessProxyProvider.GetProxy();
            var projects = workItemStore.Projects.OfType<Project>();
            var selectedProject = projects.FirstOrDefault(a => a.Name.Equals(SelectedProjectName));

            var devTeamMatrix = GetTeamCapacityByHour(proxy, workItemStore, selectedProject, iterationNode);
            return devTeamMatrix.Individuals.OrderBy(a => a.Name).Where(a => a.Capacity > 0).ToList();

            //var iterationNode = GetSelectIterationNodes(IterationTreeView.Nodes.OfType<TreeNode>()).FirstOrDefault(a => !a.HasChildNodes);
            //DEVDataGridView.AutoGenerateColumns = true;
            //if (iterationNode != null)
            //{
            //    var devTeamMatrix = GetTeamCapacityByHour(proxy, workItemStore, selectedProject, iterationNode);
            //    DEVDataGridView.DataSource = devTeamMatrix.Individuals.OrderBy(a => a.Name).Where(a => a.Capacity > 0).ToList();
            //}
        }

        public Node GetCurrentIterationNode()
        {
            var workItemStore = tfsTeamProjectCollection.GetService<WorkItemStore>();
            var commonStructureService = tfsTeamProjectCollection.GetService<ICommonStructureService4>();

            var project = workItemStore.Projects.OfType<Project>().Where(a => a.Name == SelectedProjectName).FirstOrDefault();
            var currentIterationNode = default(Node);
            var rootNodes = project.IterationRootNodes.OfType<Node>();
            foreach (Node rootNode in rootNodes)
            {
                if (rootNode.Name.Equals(TBDIterationName, StringComparison.InvariantCultureIgnoreCase))
                    continue;

                if (rootNode.Name.StartsWith("Release 2014"))
                    continue;

                foreach (Node childNode in rootNode.ChildNodes)
                {
                    var iterationInfo = commonStructureService.GetNode(childNode.Uri.ToString());
                    var hasStartDate = iterationInfo.StartDate.HasValue;
                    var hasFinishDate = iterationInfo.FinishDate.HasValue;
                    if (hasStartDate && hasFinishDate && iterationInfo.StartDate <= DateTime.Now && iterationInfo.FinishDate.Value.AddDays(1) >= DateTime.Now)
                    {
                        currentIterationNode = childNode;
                        break;
                    }
                }

                if (currentIterationNode != null)
                    break;
            }
            return currentIterationNode;
        }

        #region Protected Private Methods
        protected EmailTemplate CreateEmailTemplate(string templateName)
        {
            var xlstTemplateFilePath = Path.Combine(Path.GetDirectoryName(typeof(XsltEmailTemplate).Assembly.Location), string.Format(@"App_Data\Email Templates\{0}",templateName));
            return new XsltEmailTemplate(xlstTemplateFilePath);
        }

        protected string GetEmail(string userNameWithDomain)
        {
            var account = userNameWithDomain.Substring(userNameWithDomain.IndexOf("\\") + 1);
            var entry = new DirectoryEntry(ADLdapPath, ADUserName, ADPassword);

            try
            {
                var search = new DirectorySearcher(entry);

                // specify the search filter
                search.Filter = "(&(objectClass=user)(anr=" + account + "))";

                // specify which property values to return in the search
                search.PropertiesToLoad.Add("givenName");   // first name
                search.PropertiesToLoad.Add("sn");          // last name
                search.PropertiesToLoad.Add("mail");        // smtp mail address

                var result = search.FindOne();
                if (result != null)
                {
                    return result.Properties["mail"][0].ToString();
                }
                else
                {
                    return string.Empty;
                }
            }
            catch (Exception ex)
            {
            }
            return string.Format("{0}@visioninternet.com", account);
        }

        protected void Authenticate()
        {
            if (tfsTeamProjectCollection == null)
            {
                var domain = string.Empty;
                var userName = UserNameWithDomain;
                var index = userName.IndexOf("\\");
                if (index > 0 && index < userName.Length - 1)
                {
                    domain = userName.Substring(0, index);
                    userName = userName.Substring(index + 1, userName.Length - index - 1);
                }

                var credential = new NetworkCredential(userName, Password);
                if (!string.IsNullOrEmpty(domain))
                    credential.Domain = domain;

                var _tfsTeamProjectCollection = new TfsTeamProjectCollection(new Uri(TfsCollectionURL), credential);
                _tfsTeamProjectCollection.EnsureAuthenticated();
                if (_tfsTeamProjectCollection.HasAuthenticated)
                    tfsTeamProjectCollection = _tfsTeamProjectCollection;
            }
        }
        #endregion

        #region Static Methods
        public static DEVTeamTrackMatrix GetTeamCapacityByHour(IWebAccessProxy proxy, WorkItemStore workItemStore, Project project, Node iterationNode)
        {
            var matrix = new DEVTeamTrackMatrix();

            var list = new List<Node>();
            if (iterationNode.ChildNodes.Count > 0)
                list.AddRange(iterationNode.ChildNodes.Cast<Node>());
            else
                list.Add(iterationNode);

            var result = 0f;
            var expectedDate = DateTime.Now.Date.AddHours(-1); // Peking team.
            var developmentActivity = "Development";
            foreach (var node in list)
            {
                var team = proxy.GetTeamCapacitySettings(project, string.Empty, true, node.Path, node.Uri.Segments.Last());
                var totalDays = team.IterationEndDate.Subtract(team.IterationStartDate).Days + 1; // as the enddate always 12:00:00 AM

                var teamDaysOffDateList = GetDateList(team.TeamCapacity.TeamDaysOffDates);
                var teamDaysOff = teamDaysOffDateList.Count;

                var teamDaysAvaiableDateList = new List<DateTime>();
                for (var currentDate = team.IterationStartDate; currentDate <= team.IterationEndDate; currentDate = currentDate.AddDays(1))
                {
                    if (currentDate.DayOfWeek == DayOfWeek.Saturday || currentDate.DayOfWeek == DayOfWeek.Sunday)
                        continue;
                    if (teamDaysOffDateList.Contains(currentDate))
                        continue;
                    teamDaysAvaiableDateList.Add(currentDate);
                }

                var availableDays = teamDaysAvaiableDateList.Count;
                var finalDays = 0f;
                foreach (var member in team.TeamCapacity.TeamMemberCapacityCollection)
                {
                    if (member.Activity == developmentActivity)
                    {
                        var memberMatrix = new DEVIndividualTrackMatrix();
                        memberMatrix.Name = member.DisplayName;
                        matrix.Individuals.Add(memberMatrix);
                        var queryBuilder = new StringBuilder();
                        queryBuilder.Append("Select System.Id, System.Title from workitems where ");
                        queryBuilder.AppendFormat(" [System.TeamProject] = '{0}' ", project.Name);
                        queryBuilder.AppendFormat(" AND [System.WorkItemType]='Task' AND [System.IterationId]='{0}' ", iterationNode.Id);
                        queryBuilder.AppendFormat(" AND ([System.AssignedTo]='{0}' OR [Microsoft.VSTS.Common.ClosedBy]='{0}') ", member.DisplayName);

                        var workItemCollection = workItemStore.Query(queryBuilder.ToString());
                        foreach (WorkItem workItem in workItemCollection)
                        {
                            var trackWorkItem = new TrackMatrixWorkItem();
                            var titleField = workItem.Fields["Title"];
                            var title = (titleField.Value ?? "").ToString();
                            trackWorkItem.Title = title;

                            var stateField = workItem.Fields["State"];
                            var state = (stateField.Value ?? "").ToString();
                            trackWorkItem.State = state;

                            var estimateField = workItem.Fields["Microsoft.VSTS.Scheduling.OriginalEstimate"];
                            var estimate = 0.0f;
                            float.TryParse((estimateField.Value ?? "").ToString(), out estimate);
                            trackWorkItem.Estimate = estimate;


                            var completeField = workItem.Fields["Microsoft.VSTS.Scheduling.CompletedWork"];
                            var complete = 0.0f;
                            float.TryParse((completeField.Value ?? "").ToString(), out complete);
                            trackWorkItem.TimeSpent = complete;

                            var remainingWorkField = workItem.Fields["Microsoft.VSTS.Scheduling.RemainingWork"];
                            var remainingWork = 0.0f;
                            float.TryParse((remainingWorkField.Value ?? "").ToString(), out remainingWork);
                            trackWorkItem.RemainingWork = remainingWork;

                            memberMatrix.WorkItems.Add(trackWorkItem);
                        }
                        memberMatrix.TimeSpent = memberMatrix.WorkItems.Sum(a => (float?)a.TimeSpent) ?? 0.0f;
                        memberMatrix.TotalEstimate = memberMatrix.WorkItems.Sum(a => (float?)a.Estimate) ?? 0.0f;
                        memberMatrix.CompletedWork = memberMatrix.WorkItems.Sum(a => (float?)Math.Max(a.Estimate - a.RemainingWork, 0f)) ?? 0.0f;
                        memberMatrix.ClosedTasks = memberMatrix.WorkItems.Where(a => a.State == "Closed").Sum(a => (float?)a.Estimate) ?? 0.0f;


                        var memberDaysOffDateList = GetDateList(member.DaysOffDates);
                        var memberAvailableDays = 0;
                        var expectedWorkedDays = 0;
                        if (memberDaysOffDateList.Any())
                        {
                            foreach (var date in teamDaysAvaiableDateList)
                            {
                                if (memberDaysOffDateList.Contains(date))
                                    continue;
                                if (date.Date < expectedDate)
                                    expectedWorkedDays++;
                                memberAvailableDays++;
                            }
                        }
                        else
                        {
                            foreach (var date in teamDaysAvaiableDateList)
                            {
                                if (date.Date < expectedDate)
                                    expectedWorkedDays++;
                            }
                            memberAvailableDays = availableDays;
                        }

                        memberMatrix.Expected = member.Capacity * expectedWorkedDays;
                        memberMatrix.Capacity = member.Capacity * memberAvailableDays;
                        finalDays += memberMatrix.Capacity;
                    }
                }
                result += finalDays;
            }
            matrix.Capacity = result;
            matrix.Individuals = matrix.Individuals.OrderBy(a => a.Name).ToList();

            var teamIndividual = new DEVIndividualTrackMatrix();
            teamIndividual.Name = "_Team";
            teamIndividual.Capacity = result;
            teamIndividual.TotalEstimate = matrix.Individuals.Sum(a => (float?)a.TotalEstimate) ?? 0f;
            teamIndividual.TimeSpent = matrix.Individuals.Sum(a => (float?)a.TimeSpent) ?? 0f;
            teamIndividual.ClosedTasks = matrix.Individuals.Sum(a => (float?)a.ClosedTasks) ?? 0f;
            teamIndividual.Expected = matrix.Individuals.Sum(a => (float?)a.Expected) ?? 0f;
            teamIndividual.CompletedWork = matrix.Individuals.Sum(a => (float?)a.CompletedWork) ?? 0f;

            matrix.Individuals.Add(teamIndividual);
            return matrix;
        }

        public static List<DateTime> GetDateList(DaysOffDates[] daysOffDates)
        {
            var dateList = new List<DateTime>();
            foreach (var daysOff in daysOffDates)
            {
                for (var currentDate = daysOff.Start; currentDate <= daysOff.End; currentDate = currentDate.AddDays(1))
                {
                    if (!dateList.Contains(currentDate))
                        dateList.Add(currentDate);
                }
            }
            return dateList;
        }
        #endregion

        #region Abstract Methods
        protected abstract void ExecuteInternal(params string[] args);
        #endregion
    }
}
